/**
 * Groups an array of objects by a specified criteria
 * @param {Array} arr - The array to group
 * @param {(string|Function)} criteria - The property name to group by or a function that returns the group key
 * @returns {Object} An object where keys are the group values and values are arrays of items in that group
 * @example
 * // Group by a property
 * groupBy([{type: 'A'}, {type: 'B'}, {type: 'A'}], 'type')
 * // Returns: {A: [{type: 'A'}, {type: 'A'}], B: [{type: 'B'}]}
 * 
 * // Group by a function
 * groupBy([1, 2, 3, 4], n => n % 2 === 0 ? 'even' : 'odd')
 * // Returns: {even: [2, 4], odd: [1, 3]}
 */
export function groupBy(arr, criteria) {
    return arr.reduce(function (obj, item) {
        // Check if the criteria is a function to run on the item or a property of it
        var key =
            typeof criteria === "function" ? criteria(item) : item[criteria];

        // If the key doesn't exist yet, create it
        if (!obj.hasOwnProperty(key)) {
            obj[key] = [];
        }

        // Push the value to the object
        obj[key].push(item);

        // Return the object to the next item in the loop
        return obj;
    }, {});
};